﻿package yii.db.ar{
	import flash.data.SQLColumnSchema;
	import flash.data.SQLConnection;
	import flash.data.SQLResult;
	import flash.data.SQLSchemaResult;
	import flash.data.SQLStatement;
	import flash.data.SQLTableSchema;
	import flash.events.*;
	import flash.utils.Proxy;
	import flash.utils.flash_proxy;
	import flash.utils.getDefinitionByName;
	import flash.utils.getQualifiedClassName;


	import yii.*;
	import yii.base.*;
	import phpas3.*;
	import yii.db.*;
	import yii.db.ar.*;
	import yii.db.schema.*;


	use namespace flash_proxy;

	public class CActiveRecord extends CModel implements IEventDispatcher{




		static public var db:CDbConnection = null;
		private static var _models:Array = new Array();			// class name => model
		private var _md:CActiveRecordMetaData;								// meta data
		private var _new:Boolean =false;						// whether this instance is new or not
		private var _attributes:Object = new Object();				// attribute name => attribute value
		private var _related:Array=new Array();					// attribute name => related objects
		private var _c:CDbCriteria;								// query criteria (used by finder only)
		private var _pk;								// old primary key value
		private var _alias:String='t';						// the table alias being used for query


		public function get primaryKey():*{
			return this.getPrimaryKey();
		}
		public function set primaryKey( $value:* ):*{
			return this.setPrimaryKey( $value );
		}
		public function get tableSchema():CDbTableSchema{
			return this.getTableSchema();
		}



		/**
		 * Stores the runtime properties of related objects and arrays
		 */
		protected var relatedData:Object = {};

		/**
		 * The default SQLConnection alias used for this stored object
		 */
		public static var defaultConnectionAlias:String = "main";


		/**
		 * Stores the constructor of this class since it is not available to Proxied objects
		 */
		public var constructor:Object;

		/**
		 * This object's SQLConnection object, retrieved upon instantiation
		 */
		public var connection:SQLConnection;

		private var _className:String;
		private var eventDispatcher:EventDispatcher;

	/**
	 * Constructor+
	 * @param string $scenario scenario name+ See {@link CModel::scenario} for more details about this parameter+
	 */
	public function CActiveRecord( $scenario:String='insert' ){
		constructor = getDefinitionByName(getQualifiedClassName(this));
		eventDispatcher = new EventDispatcher(this);
		//connection = DB.getConnection(defaultConnectionAlias, true);


		if($scenario===null) // internally used by populateRecord() and model()
			return;

		this.setScenario($scenario);
		this.setIsNewRecord(true);
		this._attributes = this.getMetaData().attributeDefaults;

		this.init();

		this.attachBehaviors(this.behaviors());
		this.afterConstruct();
	}

	/**
	 * Initializes this model+
	 * This method is invoked when an AR instance is newly created and has
	 * its {@link scenario} set+
	 * You may override this method to provide code that is needed to initialize the model (e+g+ setting
	 * initial property values+)
	 * @since 1+0+8
	 */
	public function init(){
	}



	/**
	 * Returns the database connection used by active record+
	 * By default, the "db" application component is used as the database connection+
	 * You may override this method if you want to use a different database connection+
	 * @return CDbConnection the database connection used by active record+
	 */
	public function getDbConnection():CDbConnection{
		if( CActiveRecord.db ){
			return CActiveRecord.db;
		} else {
			CActiveRecord.db = Yii.app().getDb();
			if( CActiveRecord.db is CDbConnection ){
				return CActiveRecord.db;
			} else {
				throw new Error( "Active Record requires a 「db」 CDbConnection application component" );
			}
		}
	}


	/**
	 * Returns the metadata of the table that this AR belongs to
	 * @return CDbTableSchema the metadata of the table that this AR belongs to
	 */
	public function getTableSchema():CDbTableSchema{
		return this.getMetaData().tableSchema;
	}


	/**
	 * Finds a single active record with the specified condition+
	 * @param mixed $condition query condition or criteria+
	 * If a string, it is treated as query condition (the WHERE clause);
	 * If an array, it is treated as the initial values for constructing a {@link CDbCriteria} object;
	 * Otherwise, it should be an instance of {@link CDbCriteria}+
	 * @param array $params parameters to be bound to an SQL statement+
	 * This is only used when the first parameter is a string (query condition)+
	 * In other cases, please use {@link CDbCriteria::params} to set parameters+
	 * @return CActiveRecord the record found+ Null if no record is found+
	 */
	public function find( $condition:*='', $params:Array=null ):*{
		Yii.log( get_class(this)+'.find()','system.db.ar.CActiveRecord');
		var $criteria:CDbCriteria = this.getCommandBuilder().createCriteria( $condition, $params );
		return this.query( $criteria );
	}



	/**
	 * Finds all active records satisfying the specified condition+
	 * See {@link find()} for detailed explanation about $condition and $params+
	 * @param mixed $condition query condition or criteria+
	 * @param array $params parameters to be bound to an SQL statement+
	 * @return array list of active records satisfying the specified condition+ An empty array is returned if none is found+
	 */
	public function findAll( $condition:*='', $params:Array=null ):Array{
		Yii.log( get_class(this) +'.findAll()','system.db.ar.CActiveRecord' );
		var $criteria:CDbCriteria = this.getCommandBuilder().createCriteria( $condition, $params );
		return this.query( $criteria, true );
	}







	/**
	 * Returns the static model of the specified AR class+
	 * The model returned is a static instance of the AR class+
	 * It is provided for invoking class-level methods (something similar to static class methods+)
	 *
	 * EVERY derived AR class must override this method as follows,
	 * <pre>
	 * public static function model($className=__CLASS__)
	 * {
	 *     return parent::model($className);
	 * }
	 * </pre>
	 *
	 * @param string $className active record class name+
	 * @return CActiveRecord active record model instance+
	 */
	public static function model( klass:Class ):*{
		var $className:String = getQualifiedClassName( klass );
		if( CActiveRecord._models[$className] ){
			return CActiveRecord._models[$className];
		} else {
			CActiveRecord._models[$className] = new klass( null );
			var $model = CActiveRecord._models[$className];
			$model._md = new CActiveRecordMetaData( $model );
			$model.attachBehaviors( $model.behaviors() );
			return $model;
		}
	}






	/**
	 * Returns the meta-data for this AR
	 * @return CActiveRecordMetaData the meta for this AR class+
	 */
	public function getMetaData():CActiveRecordMetaData{
		if( !this._md ){
			var klass:Class = getDefinitionByName( getQualifiedClassName(this) ) as Class;
			this._md = model(klass)._md;
		}
		return this._md;
	}



	/**
	 * Finds the number of rows satisfying the specified query condition+
	 * See {@link find()} for detailed explanation about $condition and $params+
	 * @param mixed $condition query condition or criteria+
	 * @param array $params parameters to be bound to an SQL statement+
	 * @return string the number of rows satisfying the specified query condition+ Note: type is string to keep max+ precision+
	 */
	public function count( $condition:*='', $params:Array=null ):int{
		Yii.log(get_class(this)+'+count()','system.db.ar.CActiveRecord');
		var $builder:CDbCommandBuilder = this.getCommandBuilder();
		var $criteria:CDbCriteria = $builder.createCriteria($condition,$params);
		this.applyScopes($criteria);

		if( empty($criteria.with_obj) ){
			return $builder.createCountCommand(this.getTableSchema(),$criteria).queryScalar();
		} else {
			trace( "まだwithには対応していません" );
			return null;
//			var $finder:CActiveFinder = new CActiveFinder(this,$criteria.with_obj);
//			return $finder.count($criteria);
		}
	}


	/**
	 * Returns the old primary key value+
	 * This refers to the primary key value that is populated into the record
	 * after executing a find method (e+g+ find(), findAll())+
	 * The value remains unchanged even if the primary key attribute is manually assigned with a different value+
	 * @return mixed the old primary key value+ An array (column name=>column value) is returned if the primary key is composite+
	 * If primary key is not defined, null will be returned+
	 * @since 1+1+0
	 */
	public function getOldPrimaryKey():*{
		return this._pk;
	}



	/**
	 * This method is invoked after each record is instantiated by a find method+
	 * The default implementation raises the {@link onAfterFind} event+
	 * You may override this method to do postprocessing after each newly found record is instantiated+
	 * Make sure you call the parent implementation so that the event is raised properly+
	 */
	protected function afterFind():void{
//		if(this.hasEventHandler('onAfterFind'))
//			this.onAfterFind(new CEvent(this));
	}


	/**
	 * Updates records with the specified primary key(s)+
	 * See {@link find()} for detailed explanation about $condition and $params+
	 * Note, the attributes are not checked for safety and validation is NOT performed+
	 * @param mixed $pk primary key value(s)+ Use array for multiple primary keys+ For composite key, each key value must be an array (column name=>column value)+
	 * @param array $attributes list of attributes (name=>$value) to be updated
	 * @param mixed $condition query condition or criteria+
	 * @param array $params parameters to be bound to an SQL statement+
	 * @return integer the number of rows being updated
	 */
	public function updateByPk( $pk:*, $attributes:Object, $condition:*='', $params:Array=null ):int{
		trace( 22222222, $attributes );
		Yii.log( get_class(this) +'.updateByPk()', 'system.db.ar.CActiveRecord' );
		var $builder:CDbCommandBuilder = this.getCommandBuilder();
		var $table:CDbTableSchema = this.getTableSchema();
		var $criteria:CDbCriteria = $builder.createPkCriteria( $table, $pk, $condition, $params );
		var $command:CDbCommand = $builder.createUpdateCommand( $table, $attributes, $criteria );
		return $command.execute();
	}


	/**
	 * Updates the row represented by this active record+
	 * All loaded attributes will be saved to the database+
	 * Note, validation is not performed in this method+ You may call {@link validate} to perform the validation+
	 * @param array $attributes list of attributes that need to be saved+ Defaults to null,
	 * meaning all attributes that are loaded from DB will be saved+
	 * @return boolean whether the update is successful
	 * @throws CException if the record is new
	 */
	public function update($attributes:Array=null):Boolean{
		if( this.getIsNewRecord() ){
			throw new Error( "The active record cannot be updated because it is new." );
		}
		if(this.beforeSave()){
			Yii.log( get_class(this)+'.update()','system.db.ar.CActiveRecord' );
			if( this._pk === null ){
				this._pk = this.getPrimaryKey();
			}
			this.updateByPk( this.getOldPrimaryKey(), this.getAttributes($attributes) );
			this._pk = this.getPrimaryKey();
			this.afterSave();
			return true;
		} else {
			return false;
		}
		return false;
	}






	/**
	 * Sets if the record is new+
	 * @param boolean $value whether the record is new and should be inserted when calling {@link save}+
	 * @see getIsNewRecord
	 */
	public function setIsNewRecord($value:Boolean):void{
		this._new=$value;
	}



	/**
	 * This method is invoked after saving a record successfully+
	 * The default implementation raises the {@link onAfterSave} event+
	 * You may override this method to do postprocessing after record saving+
	 * Make sure you call the parent implementation so that the event is raised properly+
	 */
	protected function afterSave():void{
//		if(this.hasEventHandler('onAfterSave'))
//			this.onAfterSave(new CEvent(this));
	}


	/**
	 * Returns the primary key value+
	 * @return mixed the primary key value+ An array (column name=>column value) is returned if the primary key is composite+
	 * If primary key is not defined, null will be returned+
	 */
	public function getPrimaryKey():*{
		var $table=this.getMetaData().tableSchema;
		if(is_string($table.primaryKey))
			return this[$table.primaryKey];
		else if(is_array($table.primaryKey)){
			var $values=new Array();
			for( var index in $table.primaryKey ){
				var $name = $table.primaryKey[index];
				$values[$name]=this[$name];
			}
			return $values;
		}
		else
			return null;
	}


	/**
	 * Sets the primary key value+
	 * After calling this method, the old primary key value can be obtained from {@link oldPrimaryKey}+
	 * @param mixed $value the new primary key value+ If the primary key is composite, the new value
	 * should be provided as an array (column name=>column value)+
	 * @since 1+1+0
	 */
	public function setPrimaryKey($value):*{
		this._pk = this.getPrimaryKey();
		var $table=this.getMetaData().tableSchema;
		if(is_string($table.primaryKey))
			this[$table.primaryKey] = $value;
		else if(is_array($table.primaryKey)){
			for( var index in $table.primaryKey ){
				var $name = $table.primaryKey[index];
				this[$name] = $value[$name];
			}
		}
	}




	/**
	 * Returns all column attribute values+
	 * Note, related objects are not returned+
	 * @param mixed $names names of attributes whose value needs to be returned+
	 * If this is true (default), then all attribute values will be returned, including
	 * those that are not loaded from DB (null will be returned for those attributes)+
	 * If this is null, all attributes except those that are not loaded from DB will be returned+
	 * @return array attribute values indexed by attribute names+
	 */
	public function getAttributes( $names:*=true ):Object{
		var $attributes:Object = this._attributes;
		for( var $name in this.getMetaData().columns ){
			var $column:CDbColumnSchema = this.getMetaData().columns[$name];
			if( property_exists(this,$name) ){
				$attributes[$name]=this[$name];
			} else if($names===true && !isset($attributes[$name])){
				$attributes[$name]=null;
			}
		}
		if(is_array($names)){
			var $attrs:Object = new Object();
			for( var index in $names ){
				var $name = $names[index];
				if(property_exists(this,$name))
					$attrs[$name] = this[$name];
				else
					$attrs[$name] = isset($attributes[$name]) ? $attributes[$name] : null;
			}
			return $attrs;
		} else {
			return $attributes;
		}
	}





	/**
	 * This method is invoked before saving a record (after validation, if any)+
	 * The default implementation raises the {@link onBeforeSave} event+
	 * You may override this method to do any preparation work for record saving+
	 * Use {@link isNewRecord} to determine whether the saving is
	 * for inserting or updating record+
	 * Make sure you call the parent implementation so that the event is raised properly+
	 * @return boolean whether the saving should be executed+ Defaults to true+
	 */
	protected function beforeSave():Boolean{
		return true;
		/*
		if(this.hasEventHandler('onBeforeSave')){
			$event=new CModelEvent(this);
			this.onBeforeSave($event);
			return $event.isValid;
		} else {
			return true;
		}
		*/
	}




	/**
	 * Inserts a row into the table based on this active record attributes+
	 * If the table's primary key is auto-incremental and is null before insertion,
	 * it will be populated with the actual value after insertion+
	 * Note, validation is not performed in this method+ You may call {@link validate} to perform the validation+
	 * After the record is inserted to DB successfully, its {@link isNewRecord} property will be set false,
	 * and its {@link scenario} property will be set to be 'update'+
	 * @param array $attributes list of attributes that need to be saved+ Defaults to null,
	 * meaning all attributes that are loaded from DB will be saved+
	 * @return boolean whether the attributes are valid and the record is inserted successfully+
	 * @throws CException if the record is not new
	 */
	public function insert($attributes:Array=null):Boolean{
		if(!this.getIsNewRecord())
			trace( 'The active record cannot be inserted to database because it is not new+' );
			//throw new CDbException(Yii::t('yii','The active record cannot be inserted to database because it is not new+'));
		if(this.beforeSave()){
			Yii.log(get_class(this)+'.insert()','system.db.ar.CActiveRecord');
			var $builder:CDbCommandBuilder = this.getCommandBuilder();
			var $table:CDbTableSchema = this.getMetaData().tableSchema;
			var $command:CDbCommand = $builder.createInsertCommand( $table, this.getAttributes($attributes) );
			if( $command.execute() ){
				var $primaryKey = $table.primaryKey;
				if( $table.sequenceName!==null ){
					if(is_string($primaryKey) && this.primaryKey===null){
						this.primaryKey = $builder.getLastInsertID($table);
					} else if( is_array($primaryKey) ){
						for( var index in $primaryKey ){
							var $pk = $primaryKey[index];
							if(this[$pk]===null){
								this[$pk] = $builder.getLastInsertID($table);
								break;
							}
						}
					}
				}
				this._pk = this.getPrimaryKey();
				this.afterSave();
				this.setIsNewRecord(false);
				this.setScenario('update');
				return true;
			}
		}
		return false;
	}




	/**
	 * Returns if the current record is new+
	 * @return boolean whether the record is new and should be inserted when calling {@link save}+
	 * This property is automatically set in constructor and {@link populateRecord}+
	 * Defaults to false, but it will be set to true if the instance is created using
	 * the new operator+
	 */
	public function getIsNewRecord():Boolean{
		return this._new;
	}




	/**
	 * Saves the current record+
	 *
	 * The record is inserted as a row into the database table if its {@link isNewRecord}
	 * property is true (usually the case when the record is created using the 'new'
	 * operator)+ Otherwise, it will be used to update the corresponding row in the table
	 * (usually the case if the record is obtained using one of those 'find' methods+)
	 *
	 * Validation will be performed before saving the record+ If the validation fails,
	 * the record will not be saved+ You can call {@link getErrors()} to retrieve the
	 * validation errors+
	 *
	 * If the record is saved via insertion, its {@link isNewRecord} property will be
	 * set false, and its {@link scenario} property will be set to be 'update'+
	 * And if its primary key is auto-incremental and is not set before insertion,
	 * the primary key will be populated with the automatically generated key value+
	 *
	 * @param boolean $runValidation whether to perform validation before saving the record+
	 * If the validation fails, the record will not be saved to database+
	 * @param array $attributes list of attributes that need to be saved+ Defaults to null,
	 * meaning all attributes that are loaded from DB will be saved+
	 * @return boolean whether the saving succeeds
	 */
	public function save( $runValidation:Boolean=true, $attributes:Array=null ):Boolean{
		if(!$runValidation || this.validate($attributes)){
			return this.getIsNewRecord() ? this.insert($attributes) : this.update($attributes);
		} else {
			return false;
		}
	}










	/**
	 * Creates an active record instance.
	 * This method is called by {@link populateRecord} and {@link populateRecords}.
	 * You may override this method if the instance being created
	 * depends the attributes that are to be populated to the record.
	 * For example, by creating a record based on the value of a column,
	 * you may implement the so-called single-table inheritance mapping.
	 * @param array $attributes list of attribute values for the active records.
	 * @return CActiveRecord the active record
	 * @since 1.0.2
	 */
	protected function instantiate( $attributes:Object ):CActiveRecord{
		var $class:String = getQualifiedClassName( this );
		var klass:Class = getDefinitionByName($class) as Class;
		var $model = new klass(null);
		return $model;
	}


	/**
	 * Creates an active record with the given attributes.
	 * This method is internally used by the find methods.
	 * @param array $attributes attribute values (column name=>column value)
	 * @param boolean $callAfterFind whether to call {@link afterFind} after the record is populated.
	 * This parameter is added in version 1.0.3.
	 * @return CActiveRecord the newly created active record. The class of the object is the same as the model class.
	 * Null is returned if the input data is false.
	 */
	public function populateRecord( $attributes:Object, $callAfterFind:Boolean=true ):CActiveRecord{
		if( $attributes !== false ){
			var $record:CActiveRecord = this.instantiate( $attributes );
			$record.setScenario( 'update' );
			$record.init();
			var $md = $record.getMetaData();
			for( var $name in $attributes ){
				var $value = $attributes[$name];
				if( property_exists($record,$name) ){
					$record[$name] = $value;
				} else if( isset($md.columns[$name]) ){
					$record._attributes[$name] = $value;
				}
			}
			$record._pk = $record.getPrimaryKey();
			$record.attachBehaviors( $record.behaviors() );
			if( $callAfterFind ){
				$record.afterFind();
			}
			return $record;
		} else {
			return null;
		}
	}

	/**
	 * Creates a list of active records based on the input data.
	 * This method is internally used by the find methods.
	 * @param array $data list of attribute values for the active records.
	 * @param boolean $callAfterFind whether to call {@link afterFind} after each record is populated.
	 * This parameter is added in version 1.0.3.
	 * @param string $index the name of the attribute whose value will be used as indexes of the query result array.
	 * If null, it means the array will be indexed by zero-based integers.
	 * @return array list of active records.
	 */
	public function populateRecords($data:Array, $callAfterFind:Boolean=true, $index:String=null):Array{
		var $records:Array = new Array();
		for( var index in $data ){
			var $attributes = $data[index];
			var $record = this.populateRecord( $attributes, $callAfterFind );
			if( $record!==null ){
				if($index===null)
					$records.push( $record );
				else
					$records[$record[$index]]=$record;
			}
		}
		return $records;
	}






	/**
	 * This method is invoked before an AR finder executes a find call.
	 * The find calls include {@link find}, {@link findAll}, {@link findByPk},
	 * {@link findAllByPk}, {@link findByAttributes} and {@link findAllByAttributes}.
	 * The default implementation raises the {@link onBeforeFind} event.
	 * If you override this method, make sure you call the parent implementation
	 * so that the event is raised properly.
	 *
	 * Starting from version 1.1.5, this method may be called with a hidden {@link CDbCriteria}
	 * parameter which represents the current query criteria as passed to a find method of AR.
	 *
	 * @since 1.0.9
	 */
	protected function beforeFind(){
//		if(this.hasEventHandler('onBeforeFind')){
//			$event=new CModelEvent(this);
//			// for backward compatibility
//			$event.criteria=func_num_args()>0 ? func_get_arg(0) : null;
//			this.onBeforeFind($event);
//		}
	}







	/**
	 * Performs the actual DB query and populates the AR objects with the query result.
	 * This method is mainly internally used by other AR query methods.
	 * @param CDbCriteria $criteria the query criteria
	 * @param boolean $all whether to return all data
	 * @return mixed the AR objects populated with the query result
	 * @since 1.1.7
	 */
	protected function query( $criteria:CDbCriteria, $all:Boolean=false ):*{
        this.beforeFind();
		this.applyScopes( $criteria );
		if( empty($criteria.with_obj) ){
			if( !$all ){
				$criteria.limit = 1;
			}
			var $command:CDbCommand = this.getCommandBuilder().createFindCommand( this.getTableSchema(), $criteria );
			return $all ? this.populateRecords( $command.queryAll(), true, $criteria.index ) : this.populateRecord( $command.queryRow() );
		} else {
			Yii.log( "relationには対応していません。" );
//			var $finder = new CActiveFinder( this, $criteria.with_obj );
//			return $finder.query($criteria,$all);
		}
	}


	/**
	 * Returns the default named scope that should be implicitly applied to all queries for this model.
	 * Note, default scope only applies to SELECT queries. It is ignored for INSERT, UPDATE and DELETE queries.
	 * The default implementation simply returns an empty array. You may override this method
	 * if the model needs to be queried with some default criteria (e.g. only active records should be returned).
	 * @return array the query criteria. This will be used as the parameter to the constructor
	 * of {@link CDbCriteria}.
	 * @since 1.0.5
	 */
	public function defaultScope():Array{
		return new Array();
	}


	/**
	 * Returns the query criteria associated with this model.
	 * @param boolean $createIfNull whether to create a criteria instance if it does not exist. Defaults to true.
	 * @return CDbCriteria the query criteria that is associated with this model.
	 * This criteria is mainly used by {@link scopes named scope} feature to accumulate
	 * different criteria specifications.
	 * @since 1.0.5
	 */
	public function getDbCriteria($createIfNull:Boolean=true):CDbCriteria{
		if( this._c===null){
			var $c = this.defaultScope();
			if( $c !== new Array() || $createIfNull){
				this._c = new CDbCriteria( $c );
			}
		}
		return this._c;
	}


	/**
	 * Returns the table alias to be used by the find methods.
	 * In relational queries, the returned table alias may vary according to
	 * the corresponding relation declaration. Also, the default table alias
	 * set by {@link setTableAlias} may be overridden by the applied scopes.
	 * @param boolean $quote whether to quote the alias name
	 * @param boolean $checkScopes whether to check if a table alias is defined in the applied scopes so far.
	 * This parameter must be set false when calling this method in {@link defaultScope}.
	 * An infinite loop would be formed otherwise.
	 * @return string the default table alias
	 * @since 1.1.1
	 */
	public function getTableAlias($quote:Boolean=false, $checkScopes:Boolean=true):String{
		var $criteria:CDbCriteria = this.getDbCriteria( false );
		if( $checkScopes && $criteria !== null && $criteria.alias!='' ){
			var $alias = $criteria.alias;
		} else {
			var $alias = this._alias;
		}
		return $quote ? this.getDbConnection().getSchema().quoteTableName($alias) : $alias;
	}



	/**
	 * Finds a single active record with the specified primary key.
	 * See {@link find()} for detailed explanation about $condition and $params.
	 * @param mixed $pk primary key value(s). Use array for multiple primary keys. For composite key, each key value must be an array (column name=>column value).
	 * @param mixed $condition query condition or criteria.
	 * @param array $params parameters to be bound to an SQL statement.
	 * @return CActiveRecord the record found. Null if none is found.
	 */
	public function findByPk( $pk:*, $condition:*='',$params:Array=null):CActiveRecord{
		//Yii::trace(get_class(this).'.findByPk()','system.db.ar.CActiveRecord');
		var $prefix:String = this.getTableAlias(true) +'.';
		var $criteria:CDbCriteria = this.getCommandBuilder().createPkCriteria( this.getTableSchema(), $pk, $condition, $params, $prefix );
		return this.query( $criteria );
	}









		public function relations():Array{
			return new Array();
		}


        public function tableName():String{
                return get_class( this );
        }






//		public function getDbConnection():CDbConnection{
//			if( !CActiveRecord.db ){
//				CActiveRecord.db = new CDbConnection();
//			}
//			return CActiveRecord.db;
//		}




		public function getCommandBuilder():CDbCommandBuilder{
			return this.getDbConnection().getSchema().getCommandBuilder();
		}






		public function applyScopes( criteria ){
//			if(!empty($criteria.scopes)){
//				$scs=this.scopes();
//				$c=this.getDbCriteria();
//				foreach( (array)$criteria.scopes as $k=>$v ){
//					if(is_integer($k)){
//						if(is_string($v)){
//							if(isset($scs[$v])){
//								$c.mergeWith($scs[$v],true);
//								continue;
//							}
//							$scope=$v;
//							$params=array();
//						} else if(is_array($v)) {
//							$scope=key($v);
//							$params=current($v);
//						}
//					} else if(is_string($k)){
//						$scope=$k;
//						$params=$v;
//					}
//
//					call_user_func_array(array(this,$scope),(array)$params);
//				}
//			}
//
//			if(isset($c) || ($c=this.getDbCriteria(false))!==null){
//				$c.mergeWith($criteria);
//				$criteria=$c;
//				this._c=null;
//			}
		}










		private function getClassName():String{
			var classParts:Array = getQualifiedClassName(this).split("::");
			_className = (classParts.length == 1 ? classParts[0] : classParts[1]);
			return _className;
		}










		//////////// These are ideally static methods that would work with a subclass, however, since we
		//////////// cannot get the class of the item calling these methods statically we must make
		//////////// them not static.




		/**
		 * Creates new object, populates the attributes from the array,
		 * saves it if it validates, and returns it
		 */
		public function create(properties:Object = null):CActiveRecord{
//			var obj:ActiveRecord = new constructor();
//			obj.setDBProperties(properties);
//			obj.save();
//			return obj;
			return null;
		}



		/** EVENT DISPATCHER STUFF **/

		public function hasEventListener(type:String):Boolean
		{
			return eventDispatcher.hasEventListener(type);
		}

		public function willTrigger(type:String):Boolean
		{
			return eventDispatcher.willTrigger(type);
		}

		public function addEventListener(type:String, listener:Function, useCapture:Boolean=false, priority:int=0.0, useWeakReference:Boolean=false):void
		{
			eventDispatcher.addEventListener(type, listener, useCapture, priority, useWeakReference);
		}

		public function removeEventListener(type:String, listener:Function, useCapture:Boolean=false):void
		{
			eventDispatcher.removeEventListener(type, listener, useCapture);
		}

		public function dispatchEvent(event:Event):Boolean
		{
			return eventDispatcher.dispatchEvent(event);
		}

	}
}


















